//
// Created by meow star on 2019/9/24.
//

#ifndef RCMD_RECALL_RECALL_TYPES_H
#define RCMD_RECALL_RECALL_TYPES_H

#include <sys/types.h>
#include <sys/stat.h>
#include <json.hpp>
#include <unordered_set>
#include <vector>
#include <list>
#include <string>
#include <unordered_map>
#include <plog/Log.h>
#include <define.h>
#include <iterator>
#include <algorithm>
#include <utils.hpp>
#include <regex>

using namespace std;
using namespace nlohmann;

//class ElemIdManager;

struct ElemInfo {
    string _json_str;
    uint32_t id;
    string _item_id;
    int last_update_ts;
    ElemInfo(){
        last_update_ts = 0;
        id = -1;
    }
    /*
    int view_count;
    int digg_count;
    int comment_count;
    int real_click_count;
    int total_duration;
    int display_count;
    int share_count;

    float sexy_val;
    float hot_score;

    uint32_t publish_time;
    string from;
    string thumb;
    string coverage;
    string title;
    string source;
    vector<string> content;
    vector<string> keywords;
    vector<string> categorys;
    */
    void eleminfo(json& e) const{
        e = json::parse(_json_str);
        string extra = e[FORWARD_IDX_EXTRA].get<string>();
        try{
            std::replace(extra.begin(), extra.end(), '\'', '\"');
            e[FORWARD_IDX_EXTRA] = json::parse(extra);
        }catch (json::exception err)
        {
            spdlog::error("parse extra failed, id={}, err={}, extra={}", e[FORWARD_IDX_ID].get<string>(), err.what(), extra);
            e[FORWARD_IDX_EXTRA] = json();
        }
    }

    void set(string itemid, json* elem, const char* json_str) {
        _json_str = json_str;
        _item_id = itemid;
    }
    void brief_elem(json& e) const{
        e = json::parse(_json_str);
        e[FORWARD_IDX_THUMB] = "";
        e[FORWARD_IDX_COVERAGE] = "";
        e[FORWARD_IDX_TITLE] = "";

        e[FORWARD_IDX_CONTENT] = {};
        string extra = e[FORWARD_IDX_EXTRA].get<string>();
        try{
            std::replace(extra.begin(), extra.end(), '\'', '\"');
            e[FORWARD_IDX_EXTRA] = json::parse(extra);
        }catch (json::exception err)
        {
            spdlog::error("parse extra failed, id={}, err={}, extra={}", e[FORWARD_IDX_ID].get<string>(), err.what(), extra);
            e[FORWARD_IDX_EXTRA] = json();
        }
    }
    bool filter_pattern(string patstr) const {
        regex pattern(patstr);
        bool if_match = regex_search(_json_str, pattern);
        /*性能原因只处理标题
        if (! if_match){
            const vector<string>& keywords = eleminfo[FORWARD_IDX_KEYWORDS].get<vector<string>>();
            for(const string& keyword : keywords){
                if_match = regex_search(eleminfo[FORWARD_IDX_TITLE].get<string>(), pattern);
                if (if_match){
                    break;
                }
            }
        }
        */
        return if_match;
    }
    /*
    bool is_subset_of(string key, vector<string> origin_set) const{
        if (key == "keyword"){
            vector<string> c;
            const vector<string>& keywords = eleminfo[FORWARD_IDX_KEYWORDS].get<vector<string>>();
            set_intersection(origin_set.begin(), origin_set.end(), keywords.begin(), keywords.end(), inserter(c, c.begin()));
            return c.size() == keywords.size();
        }
        else if(key == "category"){
            vector<string> c;
            const vector<string>& categorys = eleminfo[FORWARD_IDX_CATEGORYS].get<vector<string>>();
            set_intersection(origin_set.begin(), origin_set.end(), categorys.begin(), categorys.end(), inserter(c, c.begin()));
            string so = StringUtils::stringify_array(origin_set);
            string sc = StringUtils::stringify_array(c);
            string sk = StringUtils::stringify_array(categorys);
            spdlog::info("so={}, sc={}, sk={}", so.data(), sc.data(), sk.data());
            return c.size() == categorys.size();
        }
        return false;
    }

    vector<string> keywords() const{
        return eleminfo[FORWARD_IDX_KEYWORDS].get<vector<string>>();
    }

    vector<string> category() const{
        return eleminfo[FORWARD_IDX_CATEGORYS].get<vector<string>>();
    }

    string author() const{
        return eleminfo[FORWARD_IDX_SOURCE].get<string>();
    }

    string location() const{
        return eleminfo[FORWARD_IDX_LOCATION].get<string>();
    }
    */
    const string& real_id() const;

    void clear() {
        id = -1;
        //real_id = "";
        last_update_ts = 0;
        _json_str = "";
        /*
        view_count = 0;
        digg_count = 0;
        comment_count = 0;
        real_click_count = 0;
        total_duration = 0;
        display_count = 0;
        share_count = 0;
        sexy_val = 0;
        hot_score = 0;
        from = "";
        publish_time = 0;
        thumb = "";
        coverage = "";
        title = "";
        source = "";
        content.clear();
        keywords.clear();
        categorys.clear();
        */
    }
};
/*
class ElemIdManager{
public:
    ElemIdManager()
    {
        for(uint32_t i = 0; i < MAX_LIBRARY_COUNT ; i --){
            _st_spaces.push_back(i);
            _vec_id_2_key.push_back("");
        }
    }
    const string& get_key(uint32_t id);
    uint32_t get_id(string key);
    bool has(string key);
    bool release_id(uint32_t id);
    int clear();
public:
    uint32_t alloc_id();
public:
    unordered_map<string, uint32_t> _mp_key_2_id;
    vector<string> _vec_id_2_key;
    list<uint32_t> _st_spaces;
};
*/

class ForwardIndex{
public:
    ForwardIndex(string obj_path)
    {
        _obj_path = obj_path;
        _last_reload = 0;
        _obj_timestamp = 0;
        //_id_mgr = id_mgr;
        _vec_elems.resize(MAX_LIBRARY_COUNT);
        _mp_lineno_2_itemid_busy_flag = 0;
    }
    int update_reload_flag(uint32_t ts){
        _obj_timestamp = ts;
        switch_l2i_buffer();
        return 0;
    }
    int put_elem(string key, json* json_elem, const char* json_str, uint32_t lineno, uint32_t ts);
    int check_reload_object();
    int reload_object();
    int clean_old_elem();
    //uint32_t tmpid_2_id(uint32_t tmpid);
    const ElemInfo* get_elem(uint32_t id);
    const string& lineno_2_key(uint32_t lineno);
    const ElemInfo* get_elem(const string& itemid);
    bool validate_loading_elem(uint32_t lineno, string& key);

    //这俩函数如果用auto作为返回值类型，记得加引用，不然成拷贝了
    unordered_map<uint32_t, string>& get_l2i_idle(){
        int idle = _mp_lineno_2_itemid_busy_flag == 0? 1:0;
        return _mp_lineno_2_itemid[idle];
    }
    unordered_map<uint32_t, string>& get_l2i_busy(){
        return _mp_lineno_2_itemid[_mp_lineno_2_itemid_busy_flag];
    }
    void switch_l2i_buffer(){
        _mp_lineno_2_itemid_busy_flag = _mp_lineno_2_itemid_busy_flag == 0? 1:0;
    }
public:
    string _obj_path;
    long _last_reload;
    long _obj_timestamp;
    //ElemIdManager* _id_mgr;//id=数组中的位置
    string _default_string;
    vector<ElemInfo> _vec_elems;
    uint32_t _last_update;
    int _mp_lineno_2_itemid_busy_flag;
    vector<uint32_t> _vec_tmpid_2_id;//tmpid=文件中的行号

    unordered_map<uint32_t, string> _mp_lineno_2_itemid[2];//行号->itemid映射, 双buffer
    unordered_map<string, ElemInfo*> _mp_itemid_2_item;//itemid->文章信息
};

#endif //RCMD_RECALL_RECALL_TYPES_H
